Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Node / ReticulumProjects / MeshChatX.git / files / meshchatx / src / frontend / js / rnode / transports / BluetoothTransport.js

Displaying Raw • Download


meshchatx/src/frontend/js/rnode/transports/BluetoothTransport.js f736fe3fb0c7c907f1529b1ec072b76a5ccdd760 (f736fe3f) Text, 7.76 KB

Tff7b72import Te6edf3Transport Tff7b72from Ta5d6ff"./Transport.js"Tb4b4b4;

T8b949e/**
* Bluetooth LE transport for RNode using the Nordic UART Service (NUS).
*
* RNode firmware exposes a serial-over-BLE channel using the standard NUS
* UUIDs (https://infocenter.nordicsemi.com/topic/sdk_nrf5_v17.0.2/ble_sdk_app_nus_eval.html).
* The transport adapts that GATT contract to the Web Serial-style readable/
* writable streams expected by RNode.js.
*
* Notes:
* - BLE is suitable for management commands, EEPROM operations, TNC
* configuration and bluetooth control. It is NOT usable for ESP32 or
* nRF52 bootloader flashing because those bootloaders speak UART only.
* - Web Bluetooth requires a secure context (https or localhost).
* - The MTU is small (~20 bytes by default), so writes are chunked.
*/

Tff7b72export Tff7b72const Te6edf3NUS_SERVICE_UUID Tff7b72= Ta5d6ff"6e400001-b5a3-f393-e0a9-e50e24dcca9e"Tb4b4b4;
Tff7b72export Tff7b72const Te6edf3NUS_RX_CHARACTERISTIC_UUID Tff7b72= Ta5d6ff"6e400002-b5a3-f393-e0a9-e50e24dcca9e"Tb4b4b4;
Tff7b72export Tff7b72const Te6edf3NUS_TX_CHARACTERISTIC_UUID Tff7b72= Ta5d6ff"6e400003-b5a3-f393-e0a9-e50e24dcca9e"Tb4b4b4;

Tff7b72const Te6edf3DEFAULT_WRITE_CHUNK_SIZE Tff7b72= T79c0ff20Tb4b4b4;

Tff7b72export Tff7b72default Tff7b72class Te6edf3BluetoothTransport Tff7b72extends Te6edf3Transport Tb4b4b4{
T8b949e/**
* @param {BluetoothDevice} device
* @param {{ writeChunkSize?: number, env?: object }} [options]
*/
Tff7b72constructorTb4b4b4(Te6edf3deviceTb4b4b4, Te6edf3options Tff7b72= Tb4b4b4{Tb4b4b4}Tb4b4b4) Tb4b4b4{
Tff7b72superTb4b4b4(Ta5d6ff"bluetooth"Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3deviceTb4b4b4) Tb4b4b4{
Tff7b72throw Tff7b72new Tf85149ErrorTb4b4b4(Ta5d6ff"BluetoothTransport requires a BluetoothDevice"Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tff7b72thisTb4b4b4.Te6edf3device Tff7b72= Te6edf3deviceTb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3writeChunkSize Tff7b72= Te6edf3optionsTb4b4b4.Te6edf3writeChunkSize Tff7b72|| Te6edf3DEFAULT_WRITE_CHUNK_SIZETb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3env Tff7b72= Te6edf3optionsTb4b4b4.Te6edf3env Tff7b72|| Tb4b4b4(Tff7b72typeof Tffa657window Tff7b72!== Ta5d6ff"undefined" Tff7b72? Tffa657window Tff7b72: Tffa657globalThisTb4b4b4)Tb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3gattServer Tff7b72= Tff7b72nullTb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3rxCharacteristic Tff7b72= Tff7b72nullTb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3txCharacteristic Tff7b72= Tff7b72nullTb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3_notifyHandler Tff7b72= Tff7b72nullTb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3_readableController Tff7b72= Tff7b72nullTb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3_disconnectedHandler Tff7b72= Tff7b72nullTb4b4b4;
Tb4b4b4}

T8b949e/**
* Trigger a chooser dialog for a NUS-capable RNode.
* Returns a connected BluetoothTransport (with .open() still required to
* attach streams).
*/
Tff7b72static Tff7b72async Te6edf3requestTb4b4b4(Te6edf3options Tff7b72= Tb4b4b4{Tb4b4b4}Tb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3env Tff7b72= Te6edf3optionsTb4b4b4.Te6edf3env Tff7b72|| Tb4b4b4(Tff7b72typeof Tffa657window Tff7b72!== Ta5d6ff"undefined" Tff7b72? Tffa657window Tff7b72: Tffa657globalThisTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3envTb4b4b4.Te6edf3navigatorTff7b72?Tb4b4b4.Te6edf3bluetoothTb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3err Tff7b72= Tff7b72new Tf85149ErrorTb4b4b4(Ta5d6ff"web_bluetooth_unavailable"Tb4b4b4)Tb4b4b4;
Te6edf3errTb4b4b4.Te6edf3code Tff7b72= Ta5d6ff"WEB_BLUETOOTH_UNAVAILABLE"Tb4b4b4;
Tff7b72throw Te6edf3errTb4b4b4;
Tb4b4b4}
Tff7b72let Te6edf3deviceTb4b4b4;
Tff7b72try Tb4b4b4{
Te6edf3device Tff7b72= Tff7b72await Te6edf3envTb4b4b4.Te6edf3navigatorTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3requestDeviceTb4b4b4(Tb4b4b4{
Te6edf3filtersTff7b72: Te6edf3optionsTb4b4b4.Te6edf3filters Tff7b72|| Tb4b4b4[Tb4b4b4{ Te6edf3servicesTff7b72: Tb4b4b4[Te6edf3NUS_SERVICE_UUIDTb4b4b4] Tb4b4b4}Tb4b4b4]Tb4b4b4,
Te6edf3optionalServicesTff7b72: Tb4b4b4[Te6edf3NUS_SERVICE_UUIDTb4b4b4, Tb4b4b4...Tb4b4b4(Te6edf3optionsTb4b4b4.Te6edf3optionalServices Tff7b72|| Tb4b4b4[Tb4b4b4]Tb4b4b4)Tb4b4b4]Tb4b4b4,
Tb4b4b4}Tb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3causeTb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3message Tff7b72= Te6edf3causeTff7b72?Tb4b4b4.Te6edf3message Tff7b72|| Tffa657StringTb4b4b4(Te6edf3causeTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Te6edf3causeTff7b72?Tb4b4b4.Te6edf3name Tff7b72=== Ta5d6ff"NotFoundError" Tff7b72|| Ta5d6ff/User cancelled/iTb4b4b4.Te6edf3testTb4b4b4(Te6edf3messageTb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3err Tff7b72= Tff7b72new Tf85149ErrorTb4b4b4(Ta5d6ff"no_device_selected"Tb4b4b4)Tb4b4b4;
Te6edf3errTb4b4b4.Te6edf3code Tff7b72= Ta5d6ff"NO_DEVICE_SELECTED"Tb4b4b4;
Te6edf3errTb4b4b4.Te6edf3cause Tff7b72= Te6edf3causeTb4b4b4;
Tff7b72throw Te6edf3errTb4b4b4;
Tb4b4b4}
Tff7b72const Te6edf3err Tff7b72= Tff7b72new Tf85149ErrorTb4b4b4(Te6edf3messageTb4b4b4)Tb4b4b4;
Te6edf3errTb4b4b4.Te6edf3code Tff7b72= Ta5d6ff"DEVICE_REQUEST_FAILED"Tb4b4b4;
Te6edf3errTb4b4b4.Te6edf3cause Tff7b72= Te6edf3causeTb4b4b4;
Tff7b72throw Te6edf3errTb4b4b4;
Tb4b4b4}
Tff7b72return Tff7b72new Te6edf3BluetoothTransportTb4b4b4(Te6edf3deviceTb4b4b4, Tb4b4b4{ Te6edf3env Tb4b4b4}Tb4b4b4)Tb4b4b4;
Tb4b4b4}

Tff7b72async Te6edf3openTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72!Tff7b72thisTb4b4b4.Te6edf3deviceTb4b4b4.Te6edf3gattTb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3err Tff7b72= Tff7b72new Tf85149ErrorTb4b4b4(Ta5d6ff"bluetooth_gatt_unavailable"Tb4b4b4)Tb4b4b4;
Te6edf3errTb4b4b4.Te6edf3code Tff7b72= Ta5d6ff"GATT_UNAVAILABLE"Tb4b4b4;
Tff7b72throw Te6edf3errTb4b4b4;
Tb4b4b4}

Tff7b72thisTb4b4b4.Te6edf3gattServer Tff7b72= Tff7b72await Tff7b72thisTb4b4b4.Te6edf3deviceTb4b4b4.Te6edf3gattTb4b4b4.Te6edf3connectTb4b4b4(Tb4b4b4)Tb4b4b4;

Tff7b72let Te6edf3serviceTb4b4b4;
Tff7b72try Tb4b4b4{
Te6edf3service Tff7b72= Tff7b72await Tff7b72thisTb4b4b4.Te6edf3gattServerTb4b4b4.Te6edf3getPrimaryServiceTb4b4b4(Te6edf3NUS_SERVICE_UUIDTb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3causeTb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3err Tff7b72= Tff7b72new Tf85149ErrorTb4b4b4(Ta5d6ff"nus_service_not_found"Tb4b4b4)Tb4b4b4;
Te6edf3errTb4b4b4.Te6edf3code Tff7b72= Ta5d6ff"NUS_SERVICE_NOT_FOUND"Tb4b4b4;
Te6edf3errTb4b4b4.Te6edf3cause Tff7b72= Te6edf3causeTb4b4b4;
Tff7b72await Tff7b72thisTb4b4b4.Te6edf3_safeDisconnectTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72throw Te6edf3errTb4b4b4;
Tb4b4b4}

Tff7b72try Tb4b4b4{
Tff7b72thisTb4b4b4.Te6edf3rxCharacteristic Tff7b72= Tff7b72await Te6edf3serviceTb4b4b4.Te6edf3getCharacteristicTb4b4b4(Te6edf3NUS_RX_CHARACTERISTIC_UUIDTb4b4b4)Tb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3txCharacteristic Tff7b72= Tff7b72await Te6edf3serviceTb4b4b4.Te6edf3getCharacteristicTb4b4b4(Te6edf3NUS_TX_CHARACTERISTIC_UUIDTb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3causeTb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3err Tff7b72= Tff7b72new Tf85149ErrorTb4b4b4(Ta5d6ff"nus_characteristics_missing"Tb4b4b4)Tb4b4b4;
Te6edf3errTb4b4b4.Te6edf3code Tff7b72= Ta5d6ff"NUS_CHARACTERISTICS_MISSING"Tb4b4b4;
Te6edf3errTb4b4b4.Te6edf3cause Tff7b72= Te6edf3causeTb4b4b4;
Tff7b72await Tff7b72thisTb4b4b4.Te6edf3_safeDisconnectTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72throw Te6edf3errTb4b4b4;
Tb4b4b4}

Tff7b72await Tff7b72thisTb4b4b4.Te6edf3txCharacteristicTb4b4b4.Te6edf3startNotificationsTb4b4b4(Tb4b4b4)Tb4b4b4;

Tff7b72thisTb4b4b4.Te6edf3_notifyHandler Tff7b72= Tb4b4b4(Te6edf3eventTb4b4b4) Tb4b4b4=> Tb4b4b4{
Tff7b72const Te6edf3target Tff7b72= Te6edf3eventTb4b4b4.Te6edf3targetTb4b4b4;
Tff7b72const Te6edf3value Tff7b72= Te6edf3targetTff7b72?Tb4b4b4.Te6edf3valueTb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3valueTb4b4b4) Tb4b4b4{
Tff7b72returnTb4b4b4;
Tb4b4b4}
Tff7b72const Te6edf3bytes Tff7b72= Tff7b72new Tffa657Uint8ArrayTb4b4b4(Te6edf3valueTb4b4b4.Te6edf3bufferTb4b4b4, Te6edf3valueTb4b4b4.Te6edf3byteOffsetTb4b4b4, Te6edf3valueTb4b4b4.Te6edf3byteLengthTb4b4b4)Tb4b4b4.Te6edf3sliceTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72thisTb4b4b4.Te6edf3_readableControllerTb4b4b4) Tb4b4b4{
Tff7b72try Tb4b4b4{
Tff7b72thisTb4b4b4.Te6edf3_readableControllerTb4b4b4.Te6edf3enqueueTb4b4b4(Te6edf3bytesTb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72catch Tb4b4b4{
T8b949e// controller already closed
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}Tb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3txCharacteristicTb4b4b4.Te6edf3addEventListenerTb4b4b4(Ta5d6ff"characteristicvaluechanged"Tb4b4b4, Tff7b72thisTb4b4b4.Te6edf3_notifyHandlerTb4b4b4)Tb4b4b4;

Tff7b72thisTb4b4b4.Te6edf3_disconnectedHandler Tff7b72= Tb4b4b4(Tb4b4b4) Tb4b4b4=> Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72thisTb4b4b4.Te6edf3_readableControllerTb4b4b4) Tb4b4b4{
Tff7b72try Tb4b4b4{
Tff7b72thisTb4b4b4.Te6edf3_readableControllerTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72catch Tb4b4b4{
T8b949e// ignore
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}Tb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3deviceTb4b4b4.Te6edf3addEventListenerTb4b4b4(Ta5d6ff"gattserverdisconnected"Tb4b4b4, Tff7b72thisTb4b4b4.Te6edf3_disconnectedHandlerTb4b4b4)Tb4b4b4;

Tff7b72const Te6edf3transport Tff7b72= Tff7b72thisTb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3readable Tff7b72= Tff7b72new Te6edf3ReadableStreamTb4b4b4(Tb4b4b4{
Te6edf3startTb4b4b4(Te6edf3controllerTb4b4b4) Tb4b4b4{
Te6edf3transportTb4b4b4.Te6edf3_readableController Tff7b72= Te6edf3controllerTb4b4b4;
Tb4b4b4}Tb4b4b4,
Te6edf3cancelTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3transportTb4b4b4.Te6edf3_readableController Tff7b72= Tff7b72nullTb4b4b4;
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4)Tb4b4b4;

Tff7b72const Te6edf3chunkSize Tff7b72= Tff7b72thisTb4b4b4.Te6edf3writeChunkSizeTb4b4b4;
Tff7b72const Te6edf3writeChar Tff7b72= Tff7b72thisTb4b4b4.Te6edf3rxCharacteristicTb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3writable Tff7b72= Tff7b72new Te6edf3WritableStreamTb4b4b4(Tb4b4b4{
Tff7b72async Te6edf3writeTb4b4b4(Te6edf3chunkTb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3bytes Tff7b72= Te6edf3chunk Tff7b72instanceof Tffa657Uint8Array Tff7b72? Te6edf3chunk Tff7b72: Tff7b72new Tffa657Uint8ArrayTb4b4b4(Te6edf3chunkTb4b4b4)Tb4b4b4;
Tff7b72let Te6edf3offset Tff7b72= T79c0ff0Tb4b4b4;
Tff7b72while Tb4b4b4(Te6edf3offset Tff7b72< Te6edf3bytesTb4b4b4.Te6edf3byteLengthTb4b4b4) Tb4b4b4{
Tff7b72const Te6edf3slice Tff7b72= Te6edf3bytesTb4b4b4.Te6edf3subarrayTb4b4b4(Te6edf3offsetTb4b4b4, Te6edf3offset Tff7b72+ Te6edf3chunkSizeTb4b4b4)Tb4b4b4;
Tff7b72if Tb4b4b4(Tff7b72typeof Te6edf3writeCharTb4b4b4.Te6edf3writeValueWithoutResponse Tff7b72=== Ta5d6ff"function"Tb4b4b4) Tb4b4b4{
Tff7b72await Te6edf3writeCharTb4b4b4.Te6edf3writeValueWithoutResponseTb4b4b4(Te6edf3sliceTb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72else Tb4b4b4{
Tff7b72await Te6edf3writeCharTb4b4b4.Te6edf3writeValueTb4b4b4(Te6edf3sliceTb4b4b4)Tb4b4b4;
Tb4b4b4}
Te6edf3offset Tff7b72+= Te6edf3chunkSizeTb4b4b4;
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4)Tb4b4b4;
Tb4b4b4}

Tff7b72async Te6edf3closeTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72thisTb4b4b4.Te6edf3txCharacteristic Tff7b72&& Tff7b72thisTb4b4b4.Te6edf3_notifyHandlerTb4b4b4) Tb4b4b4{
Tff7b72try Tb4b4b4{
Tff7b72thisTb4b4b4.Te6edf3txCharacteristicTb4b4b4.Te6edf3removeEventListenerTb4b4b4(Ta5d6ff"characteristicvaluechanged"Tb4b4b4, Tff7b72thisTb4b4b4.Te6edf3_notifyHandlerTb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72catch Tb4b4b4{
T8b949e// ignore
Tb4b4b4}
Tff7b72try Tb4b4b4{
Tff7b72await Tff7b72thisTb4b4b4.Te6edf3txCharacteristicTb4b4b4.Te6edf3stopNotificationsTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72catch Tb4b4b4{
T8b949e// ignore
Tb4b4b4}
Tb4b4b4}
Tff7b72if Tb4b4b4(Tff7b72thisTb4b4b4.Te6edf3device Tff7b72&& Tff7b72thisTb4b4b4.Te6edf3_disconnectedHandlerTb4b4b4) Tb4b4b4{
Tff7b72try Tb4b4b4{
Tff7b72thisTb4b4b4.Te6edf3deviceTb4b4b4.Te6edf3removeEventListenerTb4b4b4(Ta5d6ff"gattserverdisconnected"Tb4b4b4, Tff7b72thisTb4b4b4.Te6edf3_disconnectedHandlerTb4b4b4)Tb4b4b4;
Tb4b4b4} Tff7b72catch Tb4b4b4{
T8b949e// ignore
Tb4b4b4}
Tb4b4b4}
Tff7b72await Tff7b72thisTb4b4b4.Te6edf3_safeDisconnectTb4b4b4(Tb4b4b4)Tb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3readable Tff7b72= Tff7b72nullTb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3writable Tff7b72= Tff7b72nullTb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3rxCharacteristic Tff7b72= Tff7b72nullTb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3txCharacteristic Tff7b72= Tff7b72nullTb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3_notifyHandler Tff7b72= Tff7b72nullTb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3_disconnectedHandler Tff7b72= Tff7b72nullTb4b4b4;
Tff7b72thisTb4b4b4.Te6edf3_readableController Tff7b72= Tff7b72nullTb4b4b4;
Tb4b4b4}

Tff7b72async Te6edf3_safeDisconnectTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72try Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72thisTb4b4b4.Te6edf3gattServer Tff7b72&& Tff7b72thisTb4b4b4.Te6edf3gattServerTb4b4b4.Te6edf3connectedTb4b4b4) Tb4b4b4{
Tff7b72thisTb4b4b4.Te6edf3gattServerTb4b4b4.Te6edf3disconnectTb4b4b4(Tb4b4b4)Tb4b4b4;
Tb4b4b4}
Tb4b4b4} Tff7b72catch Tb4b4b4{
T8b949e// ignore
Tb4b4b4}
Tff7b72thisTb4b4b4.Te6edf3gattServer Tff7b72= Tff7b72nullTb4b4b4;
Tb4b4b4}

Te6edf3canManageDeviceTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72return Tff7b72trueTb4b4b4;
Tb4b4b4}

Te6edf3descriptionTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72return Ta5d6ff"bluetooth-le"Tb4b4b4;
Tb4b4b4}
Tb4b4b4}


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────